#ifndef _sdl_mvector_h_
#define _sdl_mvector_h_

#include <cmath>
#include <iostream>
#include <cmath>

// z jakiegoś powodu nie ma tego w Windows
#ifndef M_PI
#define M_PI        3.14159265358979323846
#endif

#ifdef PHYSX
#include "NxPhysics.h"
#endif

#include "sdl_debug.h"

#include <vector>
#include <cstdarg>

template<class data_t, int n> struct matrix;

/**
 * \brief klasa mapująca obszar bufora na wektor wybranego wymiaru
 *
 * Klasa służy jako abstrakcja obszaru pewnego dużego bufora i pozwala go traktować tak, jakby był agregatem wielu wektorów o określonym rozmiarze.
 * Przykładowo, bufor float* vertices, zawierający 30 wartości może zostać zmapowany przez 10 wektorów 3-wymiarowych, z których pierwszy jest abstrakcją pierwszych trzech liczb, a każdy n-ty liczb o numerach n,n+1,n+2.
 *
 * Taki wektor ma dwa zastosowania. Pierwsze jest klasyczne, to znaczy buffer zawiera dane zaalokowane ad hoc, czyli bufor ma rozmiar równy wymiarowi wektora (przypadek trywialny).
 * Drugie zastosowanie polega na używaniu wektora jako uchwytu do części większego zbioru, opakowanego w struktruę algebraiczną, czyli buffer jest dany odgórnie i może być większy niż wymiar wektora.
 *
 * UWAGA DO FUNKCJI ZE ZMIENNĄ LICZBĄ ARGUMENTÓW
 * (konstruktory typu mvector<float,3>(1.0,2.0,3.0), funkcja set(...))
 * Funkcje te nie potrafią dobrze rzutować typu argumentu, jeżeli dostaną daną typu int, a spodziewają się typu float albo double.
 * Dla pewności najlepiej stosować rzutowanie i jednoznaczną notację, np 1.0 albo 1.0f, a nie 1. Nie ma problemu z 1.0f i 1.0, bo floaty są w takiej funkcji promowane do double.
 *
 *
 */

template<class data_t, int n = 3 > class mvector {
    template<class _data_t, int _n>
    friend mvector<_data_t, _n> operator*(const matrix<_data_t, _n> & m, const mvector<_data_t, _n> & v);
    template<class _data_t, int _n>
    friend mvector<_data_t, _n> operator&(const mvector<_data_t, _n> &a, const mvector<_data_t, _n> &b);
    template <class _data_t, int _n>
    friend std::ostream & operator<<(std::ostream & o, const mvector<_data_t, _n> & v);

protected:
    /**
     * Właściwe dane. Jest to tablica typu C, bo taki typ danych akceptuje OpenGL
     */
    data_t * buffer;
    ///wskazuje skąd w buforze zaczyna się mapowanie (w liczbie wektorów)
    int offset;
    ///jeżeli dane są nasze (true), to je wykasujemy, a jeżeli nie to nie
    bool lock;
    ///wektor referencji, czyli mapa na obszar bufora
    std::vector<data_t*> m;

public:

    /**
     * Definicja mapującego wektora.
     * @param b dane na które mamy mapować (uwaga na rozmiar!)
     * @param of który to jest wektor z kolei (też uwaga)
     */
    mvector(data_t * b = NULL, int of = 0) : buffer(b), offset(of) {
        /*
         * domyślnie tworzymy wektor mapujący sam na siebie (rozmiar bufora = wymiar wektora)
         */

        lock = false; // krytyczne dla wycieków

        if (buffer == NULL) {
            _msg("mvector", 1, "tworzenie wektora niemapującego " << n << "-wymiarowego");
            buffer = new data_t[n];
            lock = true; // krytyczne dla wycieków
        }

        // możliwy SIGSEGV
        for (int i = 0; i < n; i++) {
            m.push_back(&buffer[n * offset + i]);
        }

        if (b == NULL) {
            for (int i = 0; i < n; i++)
                *m[i] = 0.0;
        }
    }

    /**
     * Wektor mapujący sam na siebie (trywialny).
     * Używamy odpowiedniej notacji! Jeżeli wektor jest typu float, to piszemy 1.0f, 2.0f a nie 1, 2!
     */
    mvector(data_t a, ...) {

        lock = true;
        offset = 0;

        /*
         * niech mi ktoś powie co się z tym dzieje potem, skoro mi delete robi segfaulta w destruktorze?
         * czy to po prostu zostanie w pamięci?
         */
        buffer = new data_t[n];

        for (int i = 0; i < n; i++)
            m.push_back(&buffer[n * offset + i]);

        *m[0] = a;

        va_list va;

        va_start(va, a);

        for (int i = 1; i < (n); i++) {
            //tak ma być (w sensie typ argumentu) jakby ktoś pytał
            *m[i] = static_cast<data_t> (va_arg(va, double));
        }

        _msg("mvector", 2, "tworzenie wektora z va_list " << n << "-wymiarowego: " << *this);

        va_end(va);
    }

    /* Konstruktory to zuuuuoo...
     *
     * Ten jest w miarę bezpieczny
     */

    /**
     * \brief Konstruktor kopiujący - kopiuje mapowanie i wartości
     *
     * Ten konstruktor może być mylący, bo normalnie spodziewamy się, że kopiujemy cały wektor, który jest niezależny od źródłowego.
     * Otóż tutaj jest inaczej, wektor docelowy jest wyjściowo mapą na te same dane, co źródłowy, czyli modyfikując wyjściowy, modyfikujemy
     * także dane źródłowego.
     *
     */
    mvector(const mvector & s) {
        /*		if(s.lock)
                                _msg("mvector",1,"kopiowanie wektora mapującego "<<s);

                        // niezamknięty, czyli nie nasz (bo kopiujemy buffer)
                        if(s.lock){
                                _msg("mvector",1,"NAGŁE tworzenie wektora niemapującego "<<n<<"-wymiarowego");
                                buffer = new data_t[n];
                                lock=true; // krytyczne dla wycieków
                                offset=0;
                                for(int i=0;i<n;i++)
                                        buffer[n*offset+i]=s[i];
                        }
                        else{*/
        //uwaga! tworzenie wielu map jednego zbioru!
        lock = false;
        buffer = s.buffer;
        offset = s.offset;
        //}

        for (int i = 0; i < n; i++) {
            m.push_back(&buffer[n * offset + i]);
        }
    }

    virtual ~mvector() {
        if (lock) {
            _msg("mvector", 2, "kasowanie wektora " << n << "-wymiarowego: " << *this);
            delete [] buffer;
            buffer = NULL;
        }

        m.clear();
    }

    /**
     * To jest preferowana instancjacja wartości wektora.
     *
     * Zamiast
     * @code
     * 	mvector3f a ;
     * 	a = mvector3f(1.0,2.0,3.0);
     * @endcode
     * Robimy
     * @code
     * 	mvector3f a;
     * 	a.set(1.0,2.0,3.0);
     * @endcode
     * Przypominam, że 1!=1.0 jeżeli funkcja spodziewa się typu float albo double. Jeżeli chodzi o 1.0f i 1.0 to nie ma znaczenia, bo floaty są promowane do double.
     */
    void set(data_t a, ...) {
        *m[0] = a;

        va_list va;
        va_start(va, a);

        for (int i = 1; i < n; i++) {
            *m[i] = static_cast<data_t> (va_arg(va, double));
        }

        va_end(va);
    }

    ///ustawianie wartości z tablicy

    void set(data_t * array) {
        for (int i = 0; i < n; i++) {
            *m[i] = array[i];
        }
    }

    ///zamiast normalnego konstruktora kopiującego i instancjacji...

    void set(const mvector & s) {
        for (int i = 0; i < n; i++)
            *m[i] = *s.m[i];
    }

    ///ustawianie bufora (może się przydać do podmiany mesha)

    void set_buffer(data_t * b) {
        if (lock)
            delete [] buffer;

        buffer = b;

        lock = false;
    }

    ///ustawianie offsetu

    void set_offset(int o) {
        offset = o;
    }

    /*
     * działania algebraiczne
     */
    ///przepisuje tylko wartości

    const mvector & operator=(const mvector &s) {
        _msg("mvector", 1, "przepisywanie wektora " << s);
        for (int i = 0; i < n; i++)
            *m[i] = s[i];

        return *this;
    }

    const mvector & operator=(data_t *s) {
        for (int i = 0; i < n; i++)
            *m[i] = s[i];

        return *this;
    }

    void operator+=(const mvector & v) {
        for (int i = 0; i < n; i++)
            *m[i] += *v.m[i];
    }

    void operator-=(const mvector & v) {
        for (int i = 0; i < n; i++)
            *m[i] -= *v.m[i];
    }

    void operator*=(const data_t & a) {
        for (int i = 0; i < n; i++)
            *m[i] *= a;
    }

    void operator/=(const data_t & a) {
        for (int i = 0; i < n; i++)
            *m[i] /= a;
    }

    const mvector operator+(const mvector & s) const {
        mvector rm(NULL, 0);
        for (int i = 0; i < n; i++)
            *rm.m[i] = *m[i]+*s.m[i];
        return rm;
    }

    const mvector operator-(const mvector & s) const {
        mvector rm(NULL, 0);
        for (int i = 0; i < n; i++)
            rm[i] = *m[i] - s[i];
        return rm;
    }

    const mvector operator-() const {
        mvector rm(NULL, 0);
        for (int i = 0; i < n; i++)
            rm[i] = -*m[i];
        return rm;
    }

    const mvector operator*(const data_t & a) const {
        mvector rm(NULL, 0);

        for (int i = 0; i < n; i++)
            *rm.m[i] = a * (*m[i]);

        return rm;
    }

    const mvector operator/(const data_t & a) const {
        mvector rm(NULL, 0);

        for (int i = 0; i < n; i++)
            *rm.m[i] = *m[i] / a;

        return rm;
    }

    const data_t operator*(const mvector & s) const {
        data_t N = 0.0;

        for (int i = 0; i < n; i++)
            N += *m[i]*(*s.m[i]);

        return N;
    }

    const data_t distance(const mvector & s) const {
        return std::sqrt(((*this) - s).norm());
    }

    data_t & operator[](const int k) const {
        return *m[k];
    }

    ///zwraca normę wektora (x*x)

    const data_t norm() const {
        data_t a = 0.0;

        for (int i = 0; i < n; i++)
            a += *m[i]*(*m[i]);

        return a;
    }

    ///normalizuje wektor

    const mvector & normalize() {
        data_t d = std::sqrt(norm());

        for (int i = 0; i < n; i++)
            *m[i] /= d;

        return *this;
    }

    ///wyzerowanie wektora

    const mvector& zero() {
        for (int i = 0; i < n; i++)
            *m[i] = 0.0;

        return *this;
    }

    //przydatne przy funkcjach glFUNCTIONfv
    //

    operator const data_t*() const {
        data_t * k = buffer;
        k += n*offset;
        return k;
    }
};

/**
 * \brief Specjalizacja na 3-wektor
 */

template<class data_t>

class mvector3 : public mvector<data_t, 3 > {
    template <class _data_t>
    friend mvector3<_data_t> operator^(const mvector3<_data_t> & a, const mvector3<_data_t> & b);
    template <class _data_t>
    friend mvector3<_data_t> normal_vector(const mvector3<_data_t> & a, const mvector3<_data_t> & b);
    using mvector<data_t, 3 > ::buffer;
    using mvector<data_t, 3 > ::offset;
    using mvector<data_t, 3 > ::m;
    using mvector<data_t, 3 > ::lock;

public:
    using mvector<data_t, 3 > ::norm;
    using mvector<data_t, 3 > ::operator-;
    using mvector<data_t, 3 > ::operator+;
    using mvector<data_t, 3 > ::operator[];
    using mvector<data_t, 3 > ::operator*;
    using mvector<data_t, 3 > ::operator/;
    using mvector<data_t, 3 > ::zero;
    using mvector<data_t, 3 > ::normalize;
    using mvector<data_t, 3 > ::operator+=;
    using mvector<data_t, 3 > ::operator-=;
    using mvector<data_t, 3 > ::operator/=;
    using mvector<data_t, 3 > ::operator*=;
    using mvector<data_t, 3 > ::operator const data_t*;
    using mvector<data_t, 3 > ::set;
    using mvector<data_t, 3 > ::set_buffer;
    using mvector<data_t, 3 > ::set_offset;

    /**
     * Konstruktor wektora mapującego.
     * @param b dane do zmapowania
     * @param of który to jest wektor z kolei
     */
    mvector3(data_t * b, int of) : mvector<data_t, 3 > (b, of) {
    }

    /**
     * \brief Konstruktor wektora trywialnego (najczęściej tymczasowego).
     * @param x współrzędna x
     * @param y współrzędna y
     * @param z współrzędna z
     *
     * Tutaj akurat nie ma problemu, bo sztywno rzutujemy na data_t, także inty też można wrzucać.
     */
    mvector3(data_t x, data_t y, data_t z) :
    mvector<data_t, 3 > (data_t(x), data_t(y), data_t(z)) {
    }

    ///konstruktor trywialny

    mvector3() : mvector<data_t, 3 > (NULL, 0) {
    }

    mvector3(const mvector3 & s) : mvector<data_t, 3 > (s) {
    }

    mvector3(const mvector<data_t, 3 > & s) : mvector<data_t, 3 > (s[0], s[1], s[2]) {
    }

    mvector3(const data_t * b) : mvector<data_t, 3 > (b[0], b[1], b[2]) {
    }

    virtual ~mvector3() {
        if (lock) {
            delete [] buffer;
            buffer = NULL;
        }
    }

    const mvector3 & operator=(const mvector3 &s) {
        for (int i = 0; i < 3; i++)
            *m[i] = *s.m[i];

        return *this;
    }

    const mvector3 & operator=(data_t *s) {
        for (int i = 0; i < 3; i++)
            *m[i] = s[i];

        return *this;
    }

    /*
     * te dwa operatory trzeba zdefiniować niestety
     */
    mvector3 operator-(const mvector3 & s) const {
        mvector3 rm(NULL, 0);

        for (int i = 0; i < 3; i++)
            *rm.m[i] = *m[i]-*s.m[i];

        return rm;
    }

    mvector3 operator-() const {
        mvector3 rm(NULL, 0);

        for (int i = 0; i < 3; i++)
            *rm.m[i] = -*m[i];

        return rm;
    }

#ifdef PHYSX

    /**
     * Inicjalizacja z wektora PhysX'owego.
     *
     * Odradzane, ze względu na brak mapowania. Zalecana funkcja set(const NxVec3 &) lub przypisanie.
     */
    mvector3(const NxVec3 & v) : mvector<data_t, 3 > (data_t(v.x), data_t(v.y), data_t(v.z)) {
    }

    /**
     * Zalecany sposób instancjacji.
     */
    void set(const NxVec3 & v) {
        *m[0] = data_t(v.x);
        *m[1] = data_t(v.y);
        *m[2] = data_t(v.z);
    }

    const mvector3 & operator=(const NxVec3 & v) {
        set(v);
        return *this;
    }

    ///rzutowanko

    operator NxVec3() {
        return NxVec3(*m[0], *m[1], *m[2]);
    }

#endif
};

/**
 * \brief Macierz n na n
 * Tutaj nie ma obaw, bo nie mamy powodów żeby mapować macierze :)
 *
 */
template <typename data_t = float, int n = 3 >

        struct matrix {
    template<class _data_t, int _n>
    friend std::ostream & operator<<(std::ostream & o, const matrix<_data_t, _n> & m);
    template<typename _data_t, int _n>
            friend mvector<_data_t, _n> operator*(const matrix<_data_t, _n> & m, const mvector<_data_t, _n> & v);

    typedef enum {
        Z, I
    } my_type;

protected:
    ///właściwy bufor
    data_t * m;

public:

    matrix(my_type t = Z) {
        m = new data_t[n * n];

        switch (t) {

            case Z:
            {
                for (int i = 0; i < n * n; i++)
                    m[i] = 0.0;
            }

                break;

            case I:
                id();
                break;
        }
    }

    ~matrix() {
        delete [] m;
    }

    matrix(const matrix & s) {
        m = new data_t[n * n];

        for (int i = 0; i < n * n; i++)
            m[i] = s.m[i];
    }

    matrix(const data_t * s) {
        m = new data_t[n * n];

        for (int i = 0; i < n * n; i++)
            m[i] = s[i];
    }

    matrix(data_t a, ...) {
        m = new data_t[n * n];
        m[0] = a;
        va_list va;
        va_start(va, a);

        for (int i = 1; i < (n * n); i++) {
            m[i] = static_cast<data_t> (va_arg(va, double));
        }

        va_end(va);

        transpose();
    }

    void set(data_t a, ...) {
        m[0] = a;
        va_list va;
        va_start(va, a);

        for (int i = 1; i < (n * n); i++) {
            m[i] = static_cast<data_t> (va_arg(va, double));
        }

        va_end(va);

        transpose();
    }

    /*
     * TODO:REWORK
    matrix(vector3 a, vector3 b, vector3 c){
            m=new double[n*n];
            for(int i=0;i<3;++i) m[i]=a[i];
            for(int i=3;i<6;++i) m[i]=b[i-3];
            for(int i=6;i<n*n;++i) m[i]=c[i-6];
    }
     */

    matrix zero() {
        for (int i = 0; i < n * n; i++)
            m[i] = 0.0;

        return *this;
    }

    matrix id() {
        zero();

        for (int i = 0; i < n; i++) {
            m[i * n + i] = 1.0;
        }

        return *this;
    }

    matrix diag(data_t a, ...) {
        m[0] = a;
        zero();
        va_list va;
        va_start(va, a);

        for (int i = 1; i < n; i++)
            m[i * n + i] = static_cast<data_t> (va_arg(va, double));

        va_end(va);

        return *this;
    }

    matrix transpose() {
        matrix T;

        for (int i = 0; i < n; ++i)
            for (int j = 0; j < n; ++j)
                T.m[i * n + j] = m[j * n + i];

        for (int i = 0; i < n * n; ++i)
            m[i] = T.m[i];

        return *this;
    }

    /*
     * TODO:REWORK
    double det(){
            return m[0]*m[4]*m[8] + m[1]*m[5]*m[6] + m[2]*m[3]*m[7] - m[2]*m[4]*m[6] - m[1]*m[3]*m[8] - m[0]*m[5]*m[7];
    }
     */

    /*
     * TODO: coś z tym zrobić
    double cofactor(int i,int j) {
            matrix2x2 D;
            int znak=((i+j)%2) ? -1 : 1;
            int a=0;
            for(int N=0;N<n*n;++N)
                    if(N%n!=j && (int)(N/n)!=i)
                            D[a++]=m[N];
            return znak*D.det();
    }
    matrix invert(){
            if(this->det()==0) return matrix().zero();
            matrix I;
            for(int i=0;i<n*n;++i)
                    I.m[i]=cofactor(i%n,(int)(i/n))/det();
            for(int i=0;i<n*n;++i)
                    m[i]=I.m[i];
            return *this;
    }
     */
    operator data_t * () {
        return m;
    }

    const matrix & operator=(const matrix & s) {
        for (int i = 0; i < n * n; i++)
            m[i] = s.m[i];

        return *this;
    }

    const matrix & operator=(double *s) {
        for (int i = 0; i < n * n; i++)
            m[i] = s[i];

        return *this;
    }

    matrix operator+(const matrix & s) {
        matrix rm;

        for (int i = 0; i < n * n; i++)
            rm.m[i] = m[i] + s.m[i];

        return rm;
    }

    matrix operator-(const matrix & s) {
        matrix rm;

        for (int i = 0; i < n * n; i++)
            rm.m[i] = m[i] - s.m[i];

        return rm;
    }

    matrix operator-() {
        matrix rm;

        for (int i = 0; i < n * n; i++)
            rm.m[i] = -m[i];

        return rm;
    }

    matrix operator*(const double & a) const {
        matrix rm;

        for (int i = 0; i < n * n; i++)
            rm.m[i] = a * m[i];

        return rm;
    }

    matrix operator*(const matrix & mat) const {
        matrix rm;

#pragma omp parallel for
        for (int i = 0; i < n; i++)
#pragma omp parallel for
            for (int j = 0; j < n; j++)
#pragma omp parallel for
                for (int k = 0; k < n; k++)
                    rm[i * n + j] += m[i * n + k] * mat[k * n + j];

        return rm;
    }

    matrix operator*=(const matrix & mat) {
        operator=(((*this) * mat));
        return *this;
    }

    matrix operator/(const double & a) const {
        matrix rm;

        for (int i = 0; i < n * n; i++)
            rm.m[i] = m[i] / a;

        return rm;
    }

    data_t & operator[](int k) const {
        return m[k];
    }

    mvector<data_t, n> column(const int & k) const {
        mvector<data_t, n> v(NULL, 0);
        for (int i = 0; i < n; i++) {
            v[i] = m[k * n + i];
        }
        return v;
    }

    mvector<data_t, n> row(const int & k) const {
        mvector<data_t, n> v(NULL, 0);
        for (int i = 0; i < n; i++) {
            v[i] = m[i * n + k];
        }
        return v;
    }
};


/*
 * Zewnętrzne operacje algebraiczne
 */

//WYSPECJALIZOWANE

/**
 * \brief mnożenie wektorowe
 */
template<class data_t>
mvector3<data_t> operator^(const mvector3<data_t> & a, const mvector3<data_t> & b) {
    mvector3<data_t> ret;
    ret[0] = *a.m[1]*(*b.m[2])-*a.m[2]*(*b.m[1]);
    ret[1] = *a.m[2]*(*b.m[0])-*a.m[0]*(*b.m[2]);
    ret[2] = *a.m[0]*(*b.m[1])-*a.m[1]*(*b.m[0]);
    return ret;
}

///liczenie wektora normalnego do płaszczyzny rozpinanej przez 2 wektory

template<class data_t>
mvector3<data_t> normal_vector(const mvector3<data_t> & a, const mvector3<data_t> & b) {
    mvector3<data_t> ret;
    ret = a^b;
    ret.normalize();
    return ret;
}

//SZABLONOWE

/**
 * \brief mnożenie skalarne
 */
template<typename data_t, int n>
data_t operator*(const mvector<data_t, n> & a, const mvector<data_t, n> & b) {
    double N = 0.0;

    for (int i = 0; i < n; i++)
        N += a[i] * b[i];

    return N;
}

///mnożenie liczby przez wektor

template<typename data_t, int n>
mvector<data_t, n> operator*(const data_t & a, const mvector<data_t, n> & b) {
    return b*a;
}

///mnożenie inta przez wektor

template<typename data_t, int n>
mvector<data_t, n> operator*(const int & a, const mvector<data_t, n> & b) {
    return b*a;
}

///mnożenie macierzy przez liczbę

template<typename data_t, int n>
matrix<data_t, n> operator*(const data_t & a, const matrix<data_t, n> & b) {
    return b.operator*(a);
}

///mnożenie macierzy przez liczbę int

template<typename data_t, int n>
matrix<data_t, n> operator*(const int & a, const matrix<data_t, n> & b) {
    return b.operator*(a);
}

///mnożenie macierzy przez wektor

template<typename data_t, int n>
mvector<data_t, n> operator*(const matrix<data_t, n> & m, const mvector<data_t, n> & v) {
    mvector<data_t, n> ret(NULL, 0);
    ret.zero();

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            ret[i] += v[j] * m[i * n + j];
        }
    }

    return ret;
}

///rzutowanie wektora na wektor

template<typename data_t, int n>
mvector<data_t, n> operator&(const mvector<data_t, n> &a, const mvector<data_t, n> &b) {
    mvector<data_t, n> ret(NULL, 0);
    ret = a;
    ret.normalize();
    ret *= a*b;
    return ret;
}

///operator przesunięcia do strumienia wyjścia

template<class data_t, int n>
std::ostream & operator<<(std::ostream & o, const mvector<data_t, n> & v) {

    o << "(";

    for (int i = 0; i < n; i++) {
        o << v[i];

        if (i != n - 1)
            o << ",";
    }

    o << ")";

    return o;
}

template<class data_t, int n>
std::ostream & operator<<(std::ostream & o, const matrix<data_t, n> & m) {
    for (int i = 0; i < n; i++) {
        o << "(";

        for (int j = 0; j < n; j++) {
            o << m[i * n + j] << "\t";

            if (j != n - 1)
                o << ";";

        }

        o << ")";

        if (i != n - 1) o << std::endl;
    }

    return o;
}

/*
 * Przydatne typedefy
 */
///wektor floatowy 2-w explicite
typedef mvector<float, 2 > mvector2f;
///wektor floatowy 3-w explicite
typedef mvector3<float> mvector3f;
///wektor floatowy 4-w explicite
typedef mvector<float, 4 > mvector4f;
///macierz floatowa 2x2
typedef matrix<float, 2 > matrix2x2f;
///macierz floatowa 3x3
typedef matrix<float, 3 > matrix3x3f;
///macierz floatowa 4x4
typedef matrix<float, 4 > matrix4x4f;

/*
 * funkcje nieszablonowe
 */

/**
 * \brief transformacja 3-wektora przez macierz 4x4
 * \todo uwaga - opengl chyba przyjmuje macierze, których dane są transponowane w stosunku do naszych...
 */
extern mvector3f operator*(const matrix4x4f & m, const mvector3f & v);
///ustawia macierz 4x4 z kolumn 4-wektorowych
extern matrix4x4f columns(const mvector4f & a, const mvector4f & b, const mvector4f & c, const mvector4f & d);
///ustawia macierz 4x4 z kolumn 3-wektorowych
extern matrix4x4f columns(const mvector3f & a, const mvector3f & b, const mvector3f & c);
extern matrix4x4f rotation(const mvector3f & n, const float &a);
extern matrix4x4f translation(const mvector3f & a);
extern matrix4x4f scaling(const mvector3f & a);

#endif //_sdl_mvector_h_

